Go 的管道流 Pipe
管道流 io.Pipe()
学习 HTTP 的时候看到用到了个 io.Pipe()
包,使用如下
// 流式写入
func main() {
r, w := io.Pipe()
go func() {
for i := 0; i < 100; i++ {
w.Write([]byte(fmt.Sprintf("line: %d.", i)))
}
w.Close()
}()
http.Post("localhost:8000", "text/pain", r)
}
io.Pipe()
它返回了一个 Reader 和一个 Writer,它到底能干嘛呢?
流式传输时可以使用到:
随便写个服务端
func main() {
http.HandleFunc("/report", func(rw http.ResponseWriter, r *http.Request) {
// 这个 Fprintf 可以把数据输入到指定的 IO 中
fmt.Fprintf(rw, "hello world")
})
http.ListenAndServe(":8000", nil)
}
重点是这个客户端:
pr, pw := io.Pipe()
// 开协程写入大量数据
go func(){
for i := 0; i < 100; i++ {
w.Write([]byte(fmt.Sprintf("line: %d.", i)))
}
pw.Close()
}()
// 传递Reader
http.Post("http://localhost:8000/report", "text/pain", r)
具体的工作原理看下面
这段转载自 理解golang io.Pipe:
其实 io.Pipe()
返回的不仅仅是简单的一个 Writer 一个 Reader ,它返回的是息息相关的一个 Writer 和一个 Reader。
下面我先用比较口语化的方式来讲一下它们是如何工作的。
假设:先假设我们在工地上,有两个工人,一个叫 w,一个叫 r,w 负责搬砖,而 r 负责砌墙。
初始:w 和 r 一起配合工作,一开始啥都没有,负责砌墙的r就没法工作,于是它开始睡觉(Wait)。而w只能去搬砖了。
砖来了:w 深知 r 懒惰的习性,当它把砖搬过来后,就把 r 叫醒(Signal)。然后 w 心想,反正你砌墙也要一会儿,那我也睡会儿。于是 w 叫醒 r 后它也开始睡觉(Wait)。
砌墙:r 被叫醒之后,心想着睡了这么久害怕被包工头责骂,自然就开始辛勤地砌墙了,很快就把 w 搬过来的砖用完了。r 心想,这墙砌不完可怪不到我头上,因为没砖了,于是 r 叫醒了 w,然后自己又去睡觉了。
继续搬砖:w 被叫醒后一看,哎哟我去,这么快就没砖了?然后他又跑去搬了些转过来,然后叫醒睡得跟死猪一样的r起来砌墙,自己又开始睡觉……
周而复始,直到…… w 和 r 两人就这么周而复始地配合,直到 r 发现墙砌好了,或者 w 发现工地上已经没有砖了。
以上大概就是 Pipe 的通俗的解释。不过问题也来了,这俩人瞌睡怎么这么多呢?w 干活 r 就歇着,不能同时干吗?答案是——不能
为什么?因为 Pipe 就是为了某些特定场景而提出的。Pipe 适用于,产生了一条数据,紧接着就要处理掉这条数据的场景。而且因为其内部是一把大锁,因此是线程安全的。(同时因为是阻塞的,所以要开一个协程来操作,否则两个都在一个协程里面会造成死锁)
使用例
TODO: ...